Skip to content

v0.3.0 — Goal Mode upgrades (megalist Phases 1–3)#9

Merged
willytop8 merged 30 commits into
mainfrom
wr/v0.3.0
Jun 15, 2026
Merged

v0.3.0 — Goal Mode upgrades (megalist Phases 1–3)#9
willytop8 merged 30 commits into
mainfrom
wr/v0.3.0

Conversation

@willytop8

Copy link
Copy Markdown
Owner

v0.3.0 — Goal Mode upgrades (megalist Phases 1–3)

Implements the improvements tracked in the v0.3.0 megalist. Everything is additive and backward-compatible — older state.json files load unchanged, and every new behavior is off-by-default or preserves the prior default.

What changed

Completion integrity & audit

  • Require a [goal:evidence] line to accept [goal:complete], and a concrete blocker to accept [goal:blocked]; unsubstantiated claims are rejected and re-prompted (2.1).
  • Optional separate completion auditor that verifies before archival — approve → archive, reject → restore/pause; enable with completionAudit: true or a custom auditor (2.2). Off by default.
  • Visible audit-start / audit-result messages instead of silent work; pluggable auditMessenger, disable with auditMessages: false (2.4).

Durability

  • Append-only JSONL lifecycle ledger (<stateFile>.ledger.jsonl) with state reconstruction when the main state file is missing, and fail-closed terminal-state persistence (2.3, 2.5).
  • Deterministic compaction summary built from the persisted record, not chat memory (6.3).

Auto-continue guardrails

  • No-tool-call gate: pause after consecutive tool-free continuation turns (--no-tool-turns) (5.1).
  • Pause on real user intervention ("latest instruction wins"), ignoring the plugin's own continuation prompts (5.2, 5.3).

Multiple goals

  • Multiple goals per session with /goal add, /goal list, /goal focus and per-session focus tracking (3.1, 3.2, 3.3).
  • /goal sisyphus ordered sequences that auto-promote the next goal on completion (3.4).

Schema & command UX

  • successCriteria / constraints / mode goal fields via --success / --constraints / --mode (4.1–4.3).
  • Inline --budget <n> (k/m suffixes) shorthand for the token limit (8.1).
  • Configurable commandName and registerCommand toggle (8.2).

Storage, tools & packaging

  • Project-local default state (<cwd>/.opencode/goals/state.json) + OPENCODE_GOAL_STATE_PATH env override + migration from legacy/XDG paths (6.1, 6.2).
  • Agent-facing tools get_goal / get_goal_history / set_goal / update_goal / clear_goal via the optional @opencode-ai/plugin peer dependency, loaded by dynamic import (7.1, 7.2).
  • Publish GitHub Actions workflow for npm Trusted Publishing (OIDC), publish-on-version-change (9.1).

See CHANGELOG.md (## 0.3.0) for the per-item detail.

Why

These close the largest gaps versus the upstream Goal Mode PRD and the prevalentWare/watzon implementations: completion couldn't be trusted (no evidence/audit), state wasn't durable beyond a single JSON file, only one goal per session was possible, and there was no automated release path. The changes harden unattended runs and make the plugin's release process reproducible.

Checks run

  • npm run check → 138 tests pass (was 83); 0 failures across 10 repeated runs.
  • npm run smoke → passes (tool registration / ledger / audit all degrade gracefully under persistState:false).
  • npm run pack:check → builds opencode-goal-plugin-0.3.0.tgz; package contents unchanged.
  • Node -c syntax check clean; no conflict markers.

Manual OpenCode smoke testing

Not yet performed against a live OpenCode for the new hooks. Recommended before relying on these unattended, and called out in the notes:

  • agent-tool ctx.sessionID / tool.schema shapes and the built-in completion auditor's child-session wiring are taken from OpenCode docs, not a live run;
  • audit-message delivery defaults to client.app.log pending a confirmed non-prompting conversation-message API;
  • the TUI items (1.1–1.4) are intentionally not included — OpenCode has no stable TUI plugin runtime yet.

Notes for reviewers

  • Single integration branch (15 features merged in dependency order with conflicts resolved); the completion/blocked handler is the main composition point.
  • Adds an optional peer dependency @opencode-ai/plugin (dynamic import; absence is handled).
  • State-file schema gains focused flags, archives, and orderedSessions — additive; older single-goal files still load.

willytop8 added 30 commits June 15, 2026 00:10
Add .github/workflows/publish.yml: on push to main, run the check matrix
(check/smoke/pack:check on Node 18/20/22), then publish to npm via OIDC
Trusted Publishing with no stored NPM_TOKEN. Publish-on-version-change:
package.json version is the source of truth and the publish step only runs
when that version is not yet on npm. Publish job requires id-token: write
and is gated behind a release environment.

Workflow file only; first publish + npm Trusted Publisher config left to a
human.
A [goal:complete] is now only archived when accompanied by a non-empty
[goal:evidence] line, and a [goal:blocked] only stops the goal when a
concrete blocker is stated. Unsubstantiated claims are rejected and the
plugin re-prompts for the missing evidence/blocker via a new
<evidence_required> continuation block. Evidence is stored on the result
and surfaced in /goal status and /goal history.

Adds extractCompletionEvidence, threads evidence through result
persistence, updates continuation/system/compaction/creation prompts and
the README, and adds unit + handler tests. Implements megalist item 2.1.
Add a no-tool-call gate complementing the low-output no-progress check:
track continuation turns whose assistant message has no tool/subtask
parts, and pause with reason "no tool calls" after
noToolCallTurnsBeforePause consecutive tool-free turns (default 2). Guards
against self-chat loops. Configurable via the noToolCallTurnsBeforePause
option and --no-tool-turns flag; counter is persisted and reset by
/goal resume.

Adds messageHasToolCall, threads the counter through state/persistence,
updates the README, and adds unit + handler tests. Item 5.1.
Detect a genuine human message that arrives after the plugin's most
recent continuation prompt and pause the goal (stop reason
"user intervention") so the plugin does not talk over the user; resume
via /goal resume. Plugin-generated continuation prompts (user-role
messages framed in <goal_continuation>) are ignored, and detection
requires turnCount > 0 plus a visible plugin continuation so the first
idle and scrolled-out sessions are never misread.

Adds isPluginContinuationMessage and userInterventionDetected helpers,
the idle-handler check, README/CHANGELOG, and unit + handler tests.
Implements items 5.2 and 5.3.
Goals can now carry successCriteria (--success), constraints/non-goals
(--constraints/--non-goals), and a mode of normal or ordered (--mode,
sisyphus alias). parseGoalArguments gains string/enum flag support and a
meta object; fields thread through state, persistence, the injected goal
block (escaped, new success_criteria/constraints structural tags),
creation output, and /goal status. Ordered mode adds a strict-sequence
instruction. Adds normalizeMode plus unit/parsing/command tests.
Items 4.1, 4.2, 4.3.
Resolve the state-file path as stateFilePath option > OPENCODE_GOAL_STATE_PATH
env var > project-local <cwd>/.opencode/goals/state.json (previously the
home-dir default). When the default path is empty, migrate forward on first
load from the legacy ~/.opencode-goal-plugin path and the XDG state path,
then persist to the project-local path. Explicit option/env paths are
literal with no fallback, and a corrupt primary file is preserved.

Refactors the loader into applyParsedStateFile + a candidate loop, adds
resolveStateFilePath/xdgStateFilePath/legacyStateFilePaths, README/CHANGELOG,
and unit + migration tests. Items 6.1 and 6.2.
Add a --budget shorthand for the context-token limit (--max-tokens) that
accepts a plain integer or a k/m suffix, e.g. --budget 100k == 100000.
New parseTokenBudget helper and a tokens flag type in parseGoalArguments;
invalid values are rejected with a clear error. README/CHANGELOG and unit
+ parsing tests. Item 8.1.
Add a commandName plugin option (default goal, leading slash tolerated)
so the plugin can own e.g. /objective, with all user-facing hints
following the configured name, and a registerCommand:false option that
omits the command hook entirely for programmatic-only setups. Adds
normalizeCommandOptions, parameterizes the /goal hint strings, and adds
unit + command-hook tests. Item 8.2.
…d terminal persistence

Emit every lifecycle event to an append-only JSONL ledger
(<stateFile>.ledger.jsonl, 0600) carrying the objective, so state can be
reconstructed when the main state file is missing. loadPersistedState
recovers still-active goals from the ledger (new 'reconstructed' status)
and reloads them paused. persistState now returns success/failure;
terminal (complete/blocked) transitions write the ledger event before the
state write and log on failure, so a terminal outcome survives a failed
state write (fail-closed). Ledger is tied to persistState.

Adds appendLedgerLine/readLedgerEntries/reconstructGoalsFromLedger plus
unit and end-to-end recovery tests. Items 2.3 and 2.5.
When the assistant marks a goal complete or blocked, emit an audit-start
and an audit-result message instead of acting silently. Delivery defaults
to client.app.log (visible, non-disruptive) and is pluggable via an
auditMessenger option or disable-able with auditMessages:false. Adds
defaultAuditMessenger plus handler and unit tests. Item 2.4.
buildCompactionContext now folds in a reproducible progress summary
(recent checkpoints + recent lifecycle events) reconstructed from the
goal's persisted checkpoints/history via a new
buildCompactionProgressSummary helper, instead of relying on chat memory,
and labels it as such. Adds unit tests for windowing, determinism, the
empty case, and inclusion in the compaction context. Item 6.3.
Add a sessionGoals registry so a session can hold several live goals,
keeping goalStates as the focused-goal mirror the idle handler drives.
/goal <cond> replaces the focused goal; /goal add backgrounds it and
focuses a new one (only the focused goal auto-continues). /goal list
shows numbered live goals plus a per-session archive of completed goals;
/goal focus <n|id> switches the active goal. A numeric focus ref is
treated strictly as a 1-based index (never a goalId prefix) so it can't
spuriously match a UUID. Focus is tracked per session and persisted
(state gains a per-goal focused flag and an archives array; older
single-goal files load with their goal focused).

Adds buildGoalState/formatGoalList/registry helpers and command +
persistence round-trip tests. Items 3.1, 3.2, 3.3.
Add /goal sisyphus <obj1>; <obj2>; ... to set up a strict execution
sequence: the first objective is focused and the rest queued, and the
plugin auto-promotes the next goal to focus when the focused one
completes, until the sequence is exhausted. The ordered flag is tracked
per session, shown in /goal list, persisted (orderedSessions), and
cleared by /goal clear. Adds promoteNextOrderedGoal plus command,
auto-promotion, and persistence tests. Builds on multi-goal. Item 3.4.
When a completion auditor is configured, a [goal:complete] is verified
before the goal is archived: approval archives as achieved; rejection
restores the goal (pauses it with reason 'audit rejected') instead of
archiving. Enable the built-in child-session auditor with
completionAudit:true, or supply a custom auditor function (takes
precedence). Built-in fails open without the session API; a thrown
custom auditor or ambiguous verdict fails closed. Off by default.

Adds parseAuditVerdict/buildAuditPrompt/createChildSessionAuditor plus
verdict-parsing, orchestration, and child-session-wiring tests. Item 2.2.
# Conflicts:
#	src/goal-plugin.js
# Conflicts:
#	src/goal-plugin.js
#	test/goal-plugin.test.js
# Conflicts:
#	src/goal-plugin.js
#	test/goal-plugin.test.js
# Conflicts:
#	src/goal-plugin.js
#	test/goal-plugin.test.js
# Conflicts:
#	src/goal-plugin.js
#	test/goal-plugin.test.js
# Conflicts:
#	src/goal-plugin.js
# Conflicts:
#	src/goal-plugin.js
#	test/goal-plugin.test.js
# Conflicts:
#	src/goal-plugin.js
#	test/goal-plugin.test.js
# Conflicts:
#	src/goal-plugin.js
#	test/goal-plugin.test.js
# Conflicts:
#	src/goal-plugin.js
#	test/goal-plugin.test.js
# Conflicts:
#	src/goal-plugin.js
#	test/goal-plugin.test.js
Consolidate the Unreleased changelog into a 0.3.0 section and bump
package.json to 0.3.0. Integrates the full megalist work: completion
evidence gate + optional auditor + visible audit messages, lifecycle
ledger with reconstruction and fail-closed terminal persistence,
multi-goal management + sisyphus ordered sequences, richer goal schema,
no-tool-call + user-intervention guardrails, project-local state with
migration, agent-facing tools, deterministic compaction summary, and npm
Trusted Publishing CI.
…form parity

os.homedir() ignores $HOME on macOS, so the XDG-migration test's HOME
override did not isolate the legacy ~/.opencode-goal-plugin fallback and
collided with a real user state file. Resolve the home base from an
injectable env.HOME (falling back to os.homedir()), thread env/cwd through
GoalPlugin, and make the migration test inject config instead of mutating
process globals. No production behavior change where env.HOME == real home.
@willytop8 willytop8 merged commit 2ecbf9c into main Jun 15, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant